#include "tensorflow/lite/micro/examples/hello_world/main_functions.h"

// 引入TensorFlow Lite Micro库中的一些头文件，这些头文件包含了模型、解释器、张量等相关的类和函数的定义。

#include "tensorflow/lite/micro/all_ops_resolver.h"
#include "tensorflow/lite/micro/examples/hello_world/constants.h"
#include "tensorflow/lite/micro/examples/hello_world/hello_world_model_data.h"
#include "tensorflow/lite/micro/examples/hello_world/output_handler.h"
#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/micro/micro_log.h"
#include "tensorflow/lite/micro/system_setup.h"
#include "tensorflow/lite/schema/schema_generated.h"

// 定义全局变量，用于在Arduino风格的程序中进行兼容性处理。
namespace {
const tflite::Model* model = nullptr; // 模型指针
tflite::MicroInterpreter* interpreter = nullptr; // 解释器指针
TfLiteTensor* input = nullptr; // 输入张量指针
TfLiteTensor* output = nullptr; // 输出张量指针
int inference_count = 0; // 推理计数器

constexpr int kTensorArenaSize = 2000; // 张量区域大小
alignas(16) uint8_t tensor_arena[kTensorArenaSize]; // 张量区域数组
}  // namespace

// setup函数是Arduino兼容程序的关键部分，它负责初始化模型、解释器等资源。
void setup() {
  tflite::InitializeTarget(); // 初始化目标设备

  // 将模型映射到一个可用的数据结构中，这个操作不涉及任何复制或解析，是一个非常轻量级的操作。
  model = tflite::GetModel(g_hello_world_model_data); // 获取模型数据
  if (model->version() != TFLITE_SCHEMA_VERSION) { // 检查模型版本是否与支持的版本一致
    MicroPrintf(
        "Model provided is schema version %d not equal "
        "to supported version %d.",
        model->version(), TFLITE_SCHEMA_VERSION); // 打印错误信息
    return;
  }

  // 引入所有需要的运算实现，这是一个非常轻量级的操作。
  static tflite::AllOpsResolver resolver; // 创建运算解析器对象

  // 创建一个解释器来运行模型，这个解释器会使用前面创建的运算解析器和张量区域数组。
  static tflite::MicroInterpreter static_interpreter(
      model, resolver, tensor_arena, kTensorArenaSize); // 创建解释器对象
  interpreter = &static_interpreter; // 将解释器对象的地址赋给全局解释器指针

  // 从张量区域数组中为模型的张量分配内存。
  TfLiteStatus allocate_status = interpreter->AllocateTensors(); // 分配内存
  if (allocate_status != kTfLiteOk) { // 如果分配失败，打印错误信息并返回
    MicroPrintf("AllocateTensors() failed");
    return;
  }

  // 获取模型的输入和输出张量的指针。
  input = interpreter->input(0); // 获取输入张量的指针
  output = interpreter->output(0); // 获取输出张量的指针

  // 记录已经执行的推理次数。
  inference_count = 0;
}

// loop函数是Arduino兼容程序的核心部分，它负责执行模型推理并处理结果。
void loop() {
  // 计算一个x值，用于输入到模型中。我们通过比较当前的推理计数器和每周期的推理次数来确定模型的输入范围，然后计算出一个对应的x值。
  float position = static_cast<float>(inference_count) /
                   static_cast<float>(kInferencesPerCycle); // 计算位置
  float x = position * kXrange; // 计算x值

  // 将浮点数输入转换为整数输入，并进行量化。
  int8_t x_quantized = x / input->params.scale + input->params.zero_point; // 量化输入
  // 将量化后的输入放入模型的输入张量中。
  input->data.int8[0] = x_quantized;

  // 运行推理，并报告任何错误。
  TfLiteStatus invoke_status = interpreter->Invoke(); // 运行推理
  if (invoke_status != kTfLiteOk) { // 如果推理失败，打印错误信息并返回
    MicroPrintf("Invoke failed on x: %f\n", static_cast<double>(x));
    return;
  }

  // 从模型的输出张量中获取量化后的输出，并进行反量化。
  int8_t y_quantized = output->data.int8[0]; // 获取量化后的输出
  float y = (y_quantized - output->params.zero_point) * output->params.scale; // 反量化输出

  // 输出结果。每个支持的硬件目标都可以实现一个自定义的HandleOutput函数来处理输出结果。
  HandleOutput(x, y); // 处理输出结果

  // 增加推理计数器，并在达到每周期的推理次数后重置计数器。
  inference_count += 1;
  if (inference_count >= kInferencesPerCycle) inference_count = 0;
}